Generated code - .NET remoting support
Preface
LLBLGen Pro fully support the usage of the generated code and the LLBLGen Pro runtime framework in a distributed scenario with .NET remoting. .NET remoting is often preferred in situations where the client and service are both .NET based and have a tight connection. You've two options: using normal serialization based on the .NET BinaryFormatter logic or use LLBLGen Pro's own fast serialization logic which is simply called
FastSerialization.
It's required that at both sides the same serialization option is chosen, so if you decide to use FastSerialization, be sure that both the client and the service are using FastSerialization.
LLBLGen Pro also supports XML serialization used in webservices and WCF scenarios. If you're looking for more information on XML serialization, please see the section
Using the generated code - XML Webservices support
To enable normal serialization, you don't have to do anything, it's enabled by default. Simply send the entities over the wire using remoting and you're set. All classes which are usable in a distributed scenario are serializable over remoting,
including the exceptions.
Enabling FastSerialization
To enable FastSerialization you have to set the static property
SerializationHelper.Optimization to one of the values of the enum type
SerializationOptimization, which is either None (default, use normal BinaryFormatter serialization) or Fast.
After setting this property, LLBLGen Pro will serialize all entities and other objects using FastSerialization. This means that the serialization and deserialization process is much faster and the data block to send over the wire is much smaller.
FastSerialization works for Entities, TypedLists, TypedViews and UnitOfWork2 objects. For other objects, normal serialization is used as these tend to be rather small anyway.
SerializationHelper has other optimization settings. To avoid having the serialization logic to emit for every entity a GUID (the ObjectID value of an entity) you can switch this off by setting
SerializationHelper.PreserveObjectIDs to false. The default is true. It has the side effect that an entity will receive a new GUID when deserialized. This will make using a Context object with these entities useless
as the entity isn't detectable as being the same instance.
You can also inject your own compressor object. The FastSerialization logic will create a large byte array which is placed under the key "_" in the
SerializationContext of the
BinaryFormatter. To further compress this byte array, for example with a zip library, you can set the
SerializationHelper.Compressor property to an instance of the
IByteCompressor interface, which is a simple interface to compress and decompress blocks of bytes. LLBLGen
Pro doesn't provide a basic implementation of this interface.
Serializing / Deserializing custom entity data
If you've added additional members to an entity class and you want to serialize the data in these members into the binary output as well as deserialize them at the other side, you have to add some code to make that happen. Below are the places described where to place which code to serialize / deserialize your own data with LLBLGen Pro's remoting logic. As an example, the value of a custom string member added to the entity class
OrderEntity, _orderTotal (decimal), is serialized and deserialized.
Normal serialization / deserialization
To serialize the custom member _orderTotal in the
OrderEntity class, and also to deserialize it properly, create a partial class of
OrderEntity and add the following code to that partial class. (You can also add the code to the user code region at the bottom of
OrderEntity if you want to). You don't need to add code for entity fields and entity collections inside entities, these are serialized automatically.
// C#
protected override void OnGetObjectData(SerializationInfo info, StreamingContext context)
{
info.Add("_orderTotal", _orderTotal);
}
' VB.NET
Protected Overrides Sub OnGetObjectData(info As SerializationInfo, context As StreamingContext)
info.Add("_orderTotal", _orderTotal)
End Sub
To deserialize the _orderTotal value again, add the following code to the partial class.
// C#
protected override void OnDeserialized(SerializationInfo info, StreamingContext context)
{
_orderTotal = info.GetDecimal("_orderTotal");
}
' VB.NET
Protected Overrides Sub OnDeserialized(info As SerializationInfo, context As StreamingContext)
_orderTotal = info.GetDecimal("_orderTotal")
End Sub
FastSerialization
FastSerialization doesn't use the BinaryFormatter routines of ISerializable, it uses its own graph traversal routines and therefore to be able to serialize / deserialize your own custom member variables, you can't use the OnGetObjectData and OnDeserialized methods. Instead you
have to use the following two routines, following the same example as above and these also should be added to a partial class of
OrderEntity.
Note:
|
Make sure that the serialization order and deserialization order are the same.
|
To serialize the _orderTotal value into the stream, use the following code:
// C#
protected override void SerializeOwnedData(SerializationWriter writer, object context)
{
base.SerializeOwnedData(writer, context);
writer.WriteOptimized(_orderTotal);
}
' VB.NET
Protected Overrides Sub SerializeOwnedData(writer As SerializationWriter, context As object)
MyBase.SerializeOwnedData(writer, context)
writer.WriteOptimized(_orderTotal)
End Sub
To deserialize the serialized value, use the following code:
// C#
protected override void DeserializeOwnedData(SerializationReader reader, object context)
{
base.DeserializeOwnedData(reader, context);
_orderDecimal = reader.ReadOptimizedDecimal();
}
' VB.NET
Protected Overrides Sub DeserializeOwnedData(reader As SerializationReader, context As object)
MyBase.DeserializeOwnedData(reader, context)
_orderDecimal = reader.ReadOptimizedDecimal()
End Sub
Serialize / Deserialize RemovedEntitiesTracker with FastSerialization
A
RemovedEntitiesTracker collection inside an entity collection isn't serialized into the remoting stream by default. This is because it's recommended to use a
UnitOfWork2 instance and if an entity collection with a
RemovedEntitiesTracker is added to the
UnitOfWork2 instance, as well as the
RemovedEntitiesTracker collection, it would be redundant to serialize the same collection twice.
To serialize the
RemovedEntitiesTracker inside an entity collection without a
UnitOfWork2 class, you've to add some lines of code to make this work. First you've to create a partial class of
EntityCollection<T> in the generated code. In your partial class of
EntityCollection<T>, add the code as illustrated below. After that, you can serialize over FastSerialization enabled remoting an entity collection with embedded inside itself the RemovedEntitiesTracker collection
public partial class EntityCollection<TEntity> : EntityCollectionBase2<TEntity>
where TEntity : EntityBase2, IEntity2
{
/// <summary>
/// Method which restores owned data - i.e. considered private to this collection
/// and not shared with any external object
/// </summary>
/// <param name="writer">SerializationWriter</param>
/// <param name="context">The serialization flags (previously constructed)</param>
protected override void SerializeOwnedData(SerializationWriter writer, object context)
{
base.SerializeOwnedData(writer, context);
byte[] trackerData = new byte[0];
if((this.RemovedEntitiesTracker != null) && (this.RemovedEntitiesTracker.Count>0))
{
// serialize tracker
FastSerializer serializer = new FastSerializer();
trackerData = serializer.Serialize(this.RemovedEntitiesTracker).ToArray();
}
writer.Write(trackerData);
}
/// <summary>
/// Method which restores owned data - i.e. considered private to this entity
/// and not shared with any external object
/// </summary>
/// <param name="reader">The SerializationReader containing the serialized data</param>
/// <param name="context">The serialization flags (previously read)</param>
protected override void DeserializeOwnedData(SerializationReader reader, object context)
{
base.DeserializeOwnedData(reader, context);
byte[] trackerData = reader.ReadByteArray();
if(trackerData.Length > 0)
{
// tracker data read, deserialize it to a real tracker collection
EntityCollection<TEntity> trackerCollection = new EntityCollection<TEntity>();
FastDeserializer deserializer = new FastDeserializer();
deserializer.Deserialize(trackerData, trackerCollection);
this.RemovedEntitiesTracker = trackerCollection;
}
}
}
Public Partial Class EntityCollection(Of TEntity As {EntityBase2, IEntity2})
Inherits EntityCollectionBase2(Of TEntity)
''' <summary>
''' Method which restores owned data - i.e. considered private to this collection
''' and not shared with any external object
''' </summary>
''' <param name="writer">SerializationWriter</param>
''' <param name="context">The serialization flags (previously constructed)</param>
Protected Overrides Sub SerializeOwnedData(writer As SerializationWriter, context As Object)
MyBase.SerializeOwnedData(writer, context)
Dim trackerData() As Byte
If (Not (this.RemovedEntitiesTracker Is Nothing) AndAlso (Me.RemovedEntitiesTracker.Count>0)) Then
' serialize tracker
Dim serializer As New FastSerializer()
trackerData = serializer.Serialize(Me.RemovedEntitiesTracker).ToArray()
End If
writer.Write(trackerData)
End Sub
''' <summary>
''' Method which restores owned data - i.e. considered private to this entity
''' and not shared with any external object
''' </summary>
''' <param name="reader">The SerializationReader containing the serialized data</param>
''' <param name="context">The serialization flags (previously read)</param>
Protected Overrides Sub DeserializeOwnedData(reader As SerializationReader, context As object)
MyBase.DeserializeOwnedData(reader, context)
Dim trackerData() As Byte = reader.ReadByteArray()
If trackerData.Length > 0 Then
' tracker data read, deserialize it to a real tracker collection
Dim trackerCollection As New EntityCollection(Of TEntity)()
Dim deserializer As New FastDeserializer()
deserializer.Deserialize(trackerData, trackerCollection)
Me.RemovedEntitiesTracker = trackerCollection
End If
End Sub
End Class